﻿using System;
using CTS.Com.Domain.Enum;
using CTS.Com.Domain.Exceptions;

namespace CTS.Com.Domain.Helper
{
    public static class CalcHelper
    {
        /// <summary>
        ///  Rounds a decimal value for multiple mode (default, ceiling, floor).
        /// </summary>
        /// <param name="data">A decimal number</param>
        /// <param name="position">The position of a decimal number</param>
        /// <param name="mode">Specification for how to round a decimal number.</param>
        /// <param name="rtype">The mode rounded.</param>
        /// <returns>A decimal number to be rounded.</returns>
        public static decimal CalcRound(decimal? data, int position, MidpointRounding? mode, RoundType rtype)
        {
            // Local variable declaration
            var digits = 0;
            var multiplier = decimal.One;
            var rounded = decimal.Zero;

            // Check valid
            if (mode == null && rtype == RoundType.Default) {
                throw new ParamInvalidException();
            }

            // Return decimal.Zero (data is null)
            if (DataCheckHelper.IsNull(data)) {
                return decimal.Zero;
            }

            // Get decimal value
            var value = data.Value;
            // Check position
            if (position <= 0) {
                // Get the absolute value of position
                digits = Math.Abs(position);
                // Case is ceiling or floor
                if (rtype != RoundType.Default) {
                    multiplier = (decimal)Math.Pow(10, digits);
                    value = decimal.Multiply(value, multiplier);
                    // Case is ceiling
                    if (rtype == RoundType.Ceiling) {
                        // Get rounded
                        rounded = Math.Ceiling(value);
                    }
                    // Case is floor
                    if (rtype == RoundType.Floor) {
                        // Get rounded
                        rounded = Math.Floor(value);
                    }
                    // Return value
                    return decimal.Divide(rounded, multiplier);
                }

            } else {
                // Get multiplier
                multiplier = (decimal)Math.Pow(10, position);
                value = decimal.Divide(value, multiplier);
                // Case is ceiling or floor
                if (rtype != RoundType.Default) {
                    // Get rounded (Ceiling)
                    if (rtype == RoundType.Ceiling) {
                        // Get rounded
                        rounded = Math.Ceiling(value);
                    }
                    // Get rounded (Floor)
                    if (rtype == RoundType.Floor) {
                        // Get rounded
                        rounded = Math.Floor(value);
                    }
                    // Return value
                    return decimal.Multiply(rounded, multiplier);
                }
            }

            // Case is none
            // Get rounded
            rounded = Math.Round(value, digits, mode.Value);
            // Return value
            return decimal.Multiply(rounded, multiplier);
        }

        /// <summary>
        ///  Rounds a decimal value.
        /// </summary>
        /// <param name="data">A decimal number</param>
        /// <param name="position">The position of a decimal number</param>
        /// <param name="mode">Specification for how to round a decimal number.</param>
        /// <returns>A decimal number to be rounded.</returns>
        public static decimal Round(decimal? data, int position, MidpointRounding mode)
        {
            return CalcRound(data, position, mode, RoundType.Default);
        }

        /// <summary>
        ///  Rounds a decimal value.
        /// </summary>
        /// <param name="data">A decimal number</param>
        /// <param name="position">The position of a decimal number</param>
        /// <returns>A decimal number to be rounded.</returns>
        public static decimal Ceiling(decimal? data, int position)
        {
            return CalcRound(data, position, null, RoundType.Ceiling);
        }

        /// <summary>
        ///  Rounds a decimal value.
        /// </summary>
        /// <param name="data">A decimal number</param>
        /// <param name="position">The position of a decimal number</param>
        /// <returns>A decimal number to be rounded.</returns>
        public static decimal Floor(decimal? data, int position)
        {
            return CalcRound(data, position, null, RoundType.Floor);
        }
    }
}
